package org.fujene.memcon;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import org.fujene.diskblock.DiskBlock;
import org.fujene.diskblock.PureContentDiskBlock;
import org.fujene.diskblock.SkipPointerDiskBlock;
import org.fujene.diskblock.blockwise.BlockwiseContentDiskBlock;
import org.fujene.diskblock.blockwise.BlockwiseDictionaryDiskBlock;
import org.fujene.diskblock.blockwise.BlockwiseInvertIndexDiskBlock;
import org.fujene.memoryblock.MemoryBlock;
import org.fujene.structure.Constants;
import org.fujene.structure.LookupPointer;

/**
 * ディスク情報を保持します。 メモリと同じようにIDにより管理します。
 */
public class IndexDisk{
	// ファイルを置いてあるディレクトリ
	private String dir = null;

	private SortedMap<Integer, File> fileMap = null;
	public ArrayList<LookupPointer> deletedContentsInDisk;

	public boolean isExistInList(int lookup){
		Set<Integer> keys = fileMap.keySet();

		if(keys.contains(Integer.valueOf(lookup))){ return true; }

		return false;
	}

	public SortedMap<Integer, File> getFileMap(){
		reloadFileList();
		return fileMap;
	}

	public IndexDisk(String dir){
		assert dir != null;

		fileMap = Collections.synchronizedSortedMap(new TreeMap<Integer, File>(new Comparator<Integer>(){
			@Override
			public int compare(Integer o1, Integer o2){
				return o1.intValue() - o2.intValue();
			}
		}));

		setDir(dir);
		deletedContentsInDisk = new ArrayList<LookupPointer>();
	}

	public void setDir(String dir){
		// あるか確認
		File f = new File(dir);

		if(f.exists() && f.isDirectory()){
			this.dir = dir;

			reloadFileList();
		}else if(f.exists() && !f.isDirectory()){
			System.out.println("Specified directory name exists, but not a directory.");
		}else{
			// ない
			if(f.mkdir()){
				this.dir = dir;
			}
		}
	}

	public File getDescriptor(int ID){
		return fileMap.get(Integer.valueOf(ID));
	}

	// Blockwise限定
	public DiskBlock getDiskblock(int ID){
		int blockType = getFileType(ID);

		switch(blockType){
		case Constants.MEMORY_TYPE_DICT:
			return new BlockwiseDictionaryDiskBlock(getDescriptor(ID));
		case Constants.MEMORY_TYPE_SKIPPTR:
			return new SkipPointerDiskBlock(getDescriptor(ID));
		case Constants.MEMORY_TYPE_INVINDEX:
			return new BlockwiseInvertIndexDiskBlock(getDescriptor(ID));
		case Constants.MEMORY_TYPE_CONTENTS:
			return new BlockwiseContentDiskBlock(getDescriptor(ID));
		case Constants.MEMORY_TYPE_PURECONTENT:
			return new PureContentDiskBlock(getDescriptor(ID));
		default:
			return null;
		}
	}

	// ファイル一覧更新
	public void reloadFileList(){
		File dirf = new File(dir);
		File filef = null;

		if(dirf.exists() && dirf.isDirectory()){
			// 名前から判断
			String[] fnamelist = dirf.list();
			String[] strs = null;
			Integer fileNum = null;

			// ファイルなんてなかった
			if(fnamelist == null) return;

			for(String str: fnamelist){
				strs = str.split("\\.");
				if(strs.length > 0 && strs[1].equals("blk")){
					fileNum = Integer.parseInt(strs[0]);
					if(!fileMap.containsKey(fileNum)){
						filef = new File(this.dir + str);
						fileMap.put(fileNum, filef);
					}
				}
			}
		}
	}

	public String changeNameToBak(int ID){
		File changeFile = fileMap.get(Integer.valueOf(ID));
		changeFile.renameTo(new File(changeFile.getAbsolutePath() + ".bak"));
		return changeFile.getAbsolutePath();
	}

	// 既存ファイルを消去
	public void dropFile(int ID){
		// ファイルがあるか確認
		Integer IDobj = Integer.valueOf(ID);
		if(fileMap.containsKey(IDobj)){
			File delFile = fileMap.get(IDobj);
			delFile.delete();
			fileMap.remove(IDobj);
		}
	}

	// ファイルを消す
	public void deleteFile(int blockID){
		File f = fileMap.get(Integer.valueOf(blockID));
		if(f.exists()){
			f.delete();
		}
	}

	public boolean isExist(int ID){
		Set<Integer> ids = fileMap.keySet();

		for(Integer each: ids){
			if(each.intValue() == ID) return true;
		}

		return false;
	}

	public int getFileType(int ID){
		File desc = fileMap.get(Integer.valueOf(ID));

		DiskBlock diskBlock = new DiskBlock(desc){
			@Override
			public void readToMemory(MemoryBlock memBlock){
				throw new NoSuchMethodError("Unimplemented method");
			}

			@Override
			public byte[] getData(){
				throw new NoSuchMethodError("Unimplemented method");
			}
		};

		return diskBlock.getType();
	}

	public String getDir(){
		return dir;
	}
}